package com.ycxh.module.FileStore;

import com.ycxh.module.FileStore.entity.FileFolder;
import com.ycxh.module.FileStore.entity.MyFile;
import com.ycxh.module.FileStore.util.FileUtils;
import com.ycxh.module.FileStore.util.QRCodeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 文件管理控制器
 **/
@Slf4j
@Controller
public class FileStoreController {

    // 磁盘根目录
    @Value("${file_manage.root_path:}")
    private String ROOT_PATH;

    /**
     * 上传文件
     **/
    @PostMapping("/uploadFile")
    @ResponseBody
    public Map<String, Object> uploadFile(@RequestParam("file") MultipartFile files, String path) {
        Map<String, Object> map = new HashMap<>();
        if (StringUtils.isBlank(path) || !path.startsWith(ROOT_PATH)) {
            log.error("用户没有访问当前目录的权限!上传失败...");
            map.put("code", 499);
            return map;
        }
        File file = new File(path);
        if (!file.exists() || !file.isDirectory()) {
            log.error("目录不存在!上传失败...");
            map.put("code", 499);
            return map;
        }

        // MultipartFile中getOriginalFilename()和getName方法
        // https://blog.csdn.net/qq_46225886/article/details/106379493
        // getOriginalFilename()获取的是真正的文件名，
        // getName()方法获取的并不是真正的文件名，没有后缀
        String name = files.getOriginalFilename().replaceAll(" ", "");
        // 获取当前目录下的所有文件，用来判断是否已经存在
        // File file = new File(URLDecoder.decode(myFile.getMyFilePath()));

        File desFile = new File(path, name);

        if (desFile.exists() && desFile.isFile()) {
            log.error("当前文件已存在!上传失败...");
            map.put("code", 501);
            return map;
        }

        if (!checkTarget(name)) {
            log.error("上传失败!文件名不符合规范...");
            map.put("code", 502);
            return map;
        }

        //处理文件大小
        // String size = String.valueOf(files.getSize() / 1024.0);
        //
        // int indexDot = size.lastIndexOf(".");
        // size = size.substring(0, indexDot);
        // int index = name.lastIndexOf(".");
        // String tempName = name;
        // String postfix = "";
        // int type = 4;
        // if (index != -1) {
        //     tempName = name.substring(index);
        //     name = name.substring(0, index);
        //     // 获得文件类型
        //     type = getType(tempName.toLowerCase());
        //     postfix = tempName.toLowerCase();
        // }

        try {
            files.transferTo(desFile);
            try {
                Thread.sleep(5000);
                map.put("code", 200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("文件上传失败!" + files.getOriginalFilename());
            map.put("code", 504);
        }
        return map;
    }

    /**
     * 文件下载
     **/
    @GetMapping("/downloadFile")
    public String downloadFile(@RequestParam String path, HttpServletResponse response) {
        // 获取文件信息
        File file = new File(path);
        if (file == null || !file.exists() || file.isDirectory()) {
            log.error("文件不存在");
            return "redirect:/error401Page";
        }

        String fileName = file.getName();
        try (OutputStream outputStream = response.getOutputStream()) {
            response.setCharacterEncoding("utf-8");
            // 设置返回类型
            response.setContentType("multipart/form-data");
            // 文件名转码一下，不然会出现中文乱码
            response.setHeader("Content-Disposition", "attachment;fileName=" + encodeStr(fileName));

            byte[] bytes = readBytes(file);
            if (bytes == null) {
                log.error("文件不存在");
                return "redirect:/error401Page";
            }
            outputStream.write(bytes);
            log.info("文件下载成功!" + fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }

    /**
     * 对字符串(文件名或路径)进行url编码
     */
    private String encodeStr(String str) throws Exception {
        return URLEncoder.encode(str, "UTF-8");
    }

    /**
     * 将文件转为byte数组
     */
    public byte[] readBytes(File file) throws Exception {
        long len = file.length();
        // 无论数组的类型如何，数组中的最大元素数为Integer.MAX_VALUE或大约20亿
        if (len >= 2147483647L) {
            return null;
        } else {
            byte[] bytes = new byte[(int) len];

            try (FileInputStream in = new FileInputStream(file)) {
                int readLength = in.read(bytes);
                if ((long) readLength < len) {
                    log.error("文件未读取完全");
                    return null;
                }
            } catch (Exception var10) {
                return null;
            }

            return bytes;
        }
    }


    /**
     * 删除文件
     **/
    @GetMapping("/deleteFile")
    public String deleteFile(@RequestParam String path) throws Exception {
        //获得文件信息
        File file = new File(path);
        if (file.exists() && file.isFile()) {
            file.delete();
        }

        log.info("删除文件成功!" + path);
        return "redirect:/files?path=" + encodeStr(file.getParentFile().getPath());
    }

    /**
     * 删除文件夹并清空文件
     **/
    @GetMapping("/deleteFolder")
    public String deleteFolder(@RequestParam String path) throws Exception {
        File file = new File(path);
        if (file.exists() && file.isDirectory()) {
            FileUtils.deleteFile(path, ROOT_PATH);
        }

        return file.getParentFile().equals(ROOT_PATH) ?
                "redirect:/files" :
                "redirect:/files?path=" + encodeStr(file.getParentFile().getPath());
    }


    /**
     * 添加文件夹
     **/
    @PostMapping("/addFolder")
    public String addFolder(FileFolder folder, Map<String, Object> map) throws Exception {
        if (StringUtils.isBlank(folder.getParentFolderPath()) || StringUtils.isBlank(folder.getFileFolderName())) {
            log.info("添加文件夹失败!参数缺失...");
            return "redirect:/files?error=1&path=" + URLEncoder.encode(folder.getParentFolderPath(), "UTF-8");
        }
        // 检查当前文件夹是否已经存在
        File desFile = new File(folder.getParentFolderPath() + File.separator + folder.getFileFolderName());
        if (desFile != null && desFile.exists() && desFile.isDirectory()) {
            log.info("添加文件夹失败!文件夹已存在...");
            return "redirect:/files?error=1&path=" + URLEncoder.encode(folder.getParentFolderPath(), "UTF-8");
        }
        desFile.mkdirs();
        log.info("添加文件夹成功!" + folder);
        // 刷新当前页面
        return "redirect:/files?path=" + URLEncoder.encode(folder.getParentFolderPath(), "UTF-8");
    }

    /**
     * 重命名文件夹
     **/
    @PostMapping("/updateFolder")
    public String updateFolder(FileFolder folder, Map<String, Object> map) throws Exception {
        File file = new File(folder.getPath());
        if (file.exists() && file.isDirectory()) {
            File destFile = new File(file.getParentFile().getPath() + File.separator + folder.getFileFolderName());
            if (destFile.exists() && destFile.isDirectory()) {
                log.info("重命名文件夹失败!文件夹已存在...");
                return "redirect:/files?error=2&path=" + encodeStr(file.getParentFile().getPath());
            }
            // 重命名
            file.renameTo(destFile);
        }

        log.info("重命名文件夹成功!" + folder);
        return "redirect:/files?path=" + encodeStr(file.getParentFile().getPath());
    }

    /**
     * 重命名文件
     **/
    @PostMapping("/updateFileName")
    public String updateFileName(MyFile myFile, Map<String, Object> map) throws Exception {
        File file = new File(URLDecoder.decode(myFile.getMyFilePath()));
        if (file.exists() && file.isFile()) {
            File destFile = new File(file.getParentFile().getPath() + File.separator + myFile.getMyFileName());
            if (destFile.exists() && destFile.isFile()) {
                log.info("重命名文件失败!文件已存在...");
                return "redirect:/files?error=2&path=" + encodeStr(file.getParentFile().getPath());
            }
            // 重命名
            file.renameTo(destFile);
        }

        log.info("重命名文件成功!" + myFile);
        return "redirect:/files?path=" + encodeStr(file.getParentFile().getPath());

    }

    /**
     * 获得二维码
     * 文件名有特殊字符时有问题，比如#会被截断，新建#$文本文档.txt ，path: D:\data\测试\新建
     **/
    @GetMapping("getQrCode")
    @ResponseBody
    public Map<String, Object> getQrCode(@RequestParam String path, @RequestParam String url, HttpServletRequest request) {
        Map<String, Object> map = new HashMap<>();
        map.put("imgPath", "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2654852821,3851565636&fm=26&gp=0.jpg");
        if (StringUtils.isNotBlank(path)) {
            File file = new File(path);
            if (file != null && file.exists() && file.isFile()) {
                try {
                    // springboot环境下拿到的是tomcat的临时文件的路径 https://blog.csdn.net/weixin_45535665/article/details/124087545
                    String path1 = request.getSession().getServletContext().getRealPath("/user_img/");
                    url = url + "/file/share?t=" + UUID.randomUUID().toString().substring(0, 10) + "&path=" + encodeStr(path) + "&flag=1";
                    File targetFile = new File(path1, "");
                    if (!targetFile.exists()) {
                        targetFile.mkdirs();
                    }
                    String qrCodeFileName = System.currentTimeMillis() + ".jpg";
                    File f = new File(path1, qrCodeFileName);
                    if (!f.exists()) {
                        //文件不存在,开始生成二维码并保存文件
                        OutputStream os = new FileOutputStream(f);
                        QRCodeUtil.encode(url, "/static/img/logo.png", os, true);
                        os.close();
                    }
                    map.put("imgPath", "user_img/" + qrCodeFileName);
                    map.put("url", url);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return map;
    }

    /**
     * 分享文件
     **/
    @GetMapping("/file/share")
    public String shareFile(String path, String p, String t, Integer flag, HttpServletRequest request, HttpServletResponse response) {
        String fileNameTemp = "";
        String remotePath = "";
        String fileName = "";
        Integer times = 0;
        if (flag == null || path == null || t == null) {
            log.info("下载分享文件失败，参数错误");
            return "redirect:/error400Page";
        }
        File file = new File(path);
        if (!file.exists() || file.isDirectory()) {
            return "redirect:/error400Page";
        }

        fileNameTemp = file.getName();
        try {
            //解决下载文件时 中文文件名乱码问题
            boolean isMSIE = isMSBrowser(request);
            if (isMSIE) {
                //IE浏览器的乱码问题解决
                fileNameTemp = encodeStr(fileNameTemp);
            } else {
                //万能乱码问题解决
                fileNameTemp = new String(fileNameTemp.getBytes("UTF-8"), "ISO-8859-1");
            }
            OutputStream os = new BufferedOutputStream(response.getOutputStream());
            response.setCharacterEncoding("utf-8");
            // 设置返回类型
            response.setContentType("multipart/form-data");
            // 文件名转码一下，不然会出现中文乱码
            response.setHeader("Content-Disposition", "attachment;fileName=" + fileNameTemp);

            byte[] bytes = readBytes(file);
            if (bytes == null) {
                log.error("文件不存在");
                return "redirect:/error401Page";
            }
            os.write(bytes);

            os.flush();
            os.close();
            log.info("文件下载成功!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "success";
    }


    /**
     * 根据文件的后缀名获得对应的类型
     *
     * @return int 1:文本类型   2:图像类型  3:视频类型  4:音乐类型  5:其他类型
     **/
    public int getType(String type) {
        if (".chm".equals(type) || ".txt".equals(type) || ".xmind".equals(type) || ".xlsx".equals(type) || ".md".equals(type)
                || ".doc".equals(type) || ".docx".equals(type) || ".pptx".equals(type)
                || ".wps".equals(type) || ".word".equals(type) || ".html".equals(type) || ".pdf".equals(type)) {
            return 1;
        } else if (".bmp".equals(type) || ".gif".equals(type) || ".jpg".equals(type) || ".ico".equals(type) || ".vsd".equals(type)
                || ".pic".equals(type) || ".png".equals(type) || ".jepg".equals(type) || ".jpeg".equals(type) || ".webp".equals(type)
                || ".svg".equals(type)) {
            return 2;
        } else if (".avi".equals(type) || ".mov".equals(type) || ".qt".equals(type)
                || ".asf".equals(type) || ".rm".equals(type) || ".navi".equals(type) || ".wav".equals(type)
                || ".mp4".equals(type) || ".mkv".equals(type) || ".webm".equals(type)) {
            return 3;
        } else if (".mp3".equals(type) || ".wma".equals(type)) {
            return 4;
        } else {
            return 5;
        }
    }

    /**
     * 正则验证文件名是否合法 [汉字,字符,数字,下划线,英文句号,横线]
     **/
    public boolean checkTarget(String target) {
        final String format = "[^\\u4E00-\\u9FA5\\uF900-\\uFA2D\\w-_.]";
        Pattern pattern = Pattern.compile(format);
        Matcher matcher = pattern.matcher(target);
        return !matcher.find();
    }

    /**
     * 判断当前浏览器是否为ie
     **/
    public static boolean isMSBrowser(HttpServletRequest request) {
        String[] IEBrowserSignals = {"MSIE", "Trident", "Edge"};
        String userAgent = request.getHeader("User-Agent");
        for (String signal : IEBrowserSignals) {
            if (userAgent.contains(signal)) {
                return true;
            }
        }
        return false;
    }

}
